/**
 * @file gString.c
 * @author your name (you@domain.com)
 * @brief
 * @version 0.1
 * @date 2024-10-25
 *
 * @copyright Copyright (c) 2024
 *
 */
#include "gString.h"
#include "stdio.h"
#include "string.h"
#include "stdint.h"

void gStringKmpNext(void *data, int data_len, void *next)
{
    int i = 0;
    int j = 0;

    const uint8_t *data_p = data;
    uint8_t *const next_p = (uint8_t *)next;

    if (data_p == NULL || next_p == NULL || (data_len <= 0))
    {
        return;
    }

    next_p[i++] = 0;
    for (; i < data_len; i++)
    {
        while (j > 0 && data_p[j] != data_p[i])
        {
            j = next_p[--j];
        }
        if (data_p[j] == data_p[i])
        {
            j++;
        }
        next_p[i] = j;
    }
}

static const char *gKmpCompare(void *src, int src_len, void *dst, int dst_len, void *next, kmp_return_type_t type)
{

    int i = 0;
    int index = 0;
    int cycle_count = src_len;
    const uint8_t *src_p = src;
    const uint8_t *dst_p = dst;
    const uint8_t *next_p = next;

    if (NULL == src_p || NULL == dst_p || NULL == next_p || cycle_count <= 0)
    {
        return NULL;
    }

    for (; i <= cycle_count; i++)
    {
        while (index != 0 && src_p[i] != dst_p[index])
        {
            index = next_p[--index];
        }
        if (src_p[i] == dst_p[index])
        {
            index++;
            if (index == dst_len)
            {
                if (KMP_RETURN_HEAD == type)
                {
                    return &src_p[i - (--dst_len)];
                }
                else
                {
                    return &src_p[i];
                }
            }
        }
    }

    return NULL;
}

char *gStringCompare(char *src, char *dst)
{
    const char *s = src;
    const char *d = dst;
    const char *p = src;

    if (NULL == s || NULL == d)
    {
        return NULL;
    }

    while (*p)
    {
        s = p;
        d = dst;
        while (*s != '\0' && *d != '\0' && *s == *d)
        {
            s++;
            d++;
        }

        if (*d == '\0')
        {
            return (char *)p;
        }
        p++;
    }

    return NULL;
}

char *gKmpCommonCompareH(void *src, int src_len, void *dst, int dst_len, void *next)
{
    return (char *)gKmpCompare(src, src_len, dst, dst_len, next, KMP_RETURN_HEAD);
}

char *gKmpCommonCompareE(void *src, int src_len, void *dst, int dst_len, void *next)
{
    return (char *)gKmpCompare(src, src_len, dst, dst_len, next, KMP_RETURN_END);
}

char *gKmpStringCompareH(char *src, char *dst, void *next)
{
    return (char *)gKmpCommonCompareH(src, strlen(src), dst, strlen(dst), next);
}

char *gKmpStringCompareE(char *src, char *dst, void *next)
{
    return (char *)gKmpCommonCompareE(src, strlen(src), dst, strlen(dst), next);
}

int gHexToString(void *src, int src_len, void *dst)
{
    int i = 0;
    int num = 0;
    int data_len = src_len;
    uint8_t convert_num = 0;
    uint8_t convert_data[2] = {0};
    uint8_t *data_dst = (uint8_t *)dst;
    uint8_t *data_src = (uint8_t *)src;

    if (NULL == data_dst || NULL == data_src || data_len <= 0)
    {
        return 0;
    }

    for (; i < data_len; i++)
    {
        convert_data[0] = (data_src[i] >> 4 & 0x0F);
        convert_data[1] = (data_src[i] & 0x0F);

        for (convert_num = 0; convert_num < 2; convert_num++)
        {
            if (convert_data[convert_num] >= 0 && convert_data[convert_num] <= 9)
            {
                data_dst[num++] = convert_data[convert_num] + '0';
            }
            else
            {
                data_dst[num++] = convert_data[convert_num] + 'A' - 0x0A;
            }
        }
    }

    return num;
}

static int gStringConvertHexLittleEndian(char *data, uint8_t *temp)
{
    uint8_t data_temp = 0;
    const char *convert_data = data;

    if (NULL == data || NULL == temp)
    {
        return -1;
    }

    if ((convert_data[1] >= '0') && (convert_data[1] <= '9'))
    {
        data_temp = (convert_data[1] - '0') << 4;
    }
    else if ((convert_data[1] >= 'A') && (convert_data[1] <= 'F'))
    {
        data_temp = ((convert_data[1] - 'A') + 10) << 4;
    }
    else
    {
        data_temp = ((convert_data[1] - 'a') + 10) << 4;
    }

    if ((convert_data[0] >= '0') && (convert_data[0] <= '9'))
    {
        data_temp |= (convert_data[0] - '0');
    }
    else if ((convert_data[0] >= 'A') && (convert_data[0] <= 'F'))
    {
        data_temp |= ((convert_data[0] - 'A') + 10);
    }
    else
    {
        data_temp |= ((convert_data[0] - 'a') + 10);
    }

    *temp = data_temp;

    return 0;
}


static int gStringConvertHexBigEndian(char *data, uint8_t *temp)
{
    uint8_t data_temp = 0;
    const char *convert_data = data;

    if (NULL == data || NULL == temp)
    {
        return -1;
    }

    if ((convert_data[0] >= '0') && (convert_data[0] <= '9'))
    {
        data_temp = (convert_data[0] - '0') << 4;
    }
    else if ((convert_data[0] >= 'A') && (convert_data[0] <= 'F'))
    {
        data_temp = ((convert_data[0] - 'A') + 10) << 4;
    }
    else
    {
        data_temp = ((convert_data[0] - 'a') + 10) << 4;
    }

    if ((convert_data[1] >= '0') && (convert_data[1] <= '9'))
    {
        data_temp |= (convert_data[1] - '0');
    }
    else if ((convert_data[1] >= 'A') && (convert_data[1] <= 'F'))
    {
        data_temp |= ((convert_data[1] - 'A') + 10);
    }
    else
    {
        data_temp |= ((convert_data[1] - 'a') + 10);
    }

    *temp = data_temp;

    return 0;
}

static int gStringToHexCommon(const char *src,uint8_t *dst,string_to_hex_type_t type)
{
    int num = 0;
    int data_len = 0;
    char data_temp = 0;
    char convert_num = 0;
    char convert_data[2] = {0};

    data_len = strlen(src);

    for (int i = 0; i < data_len; i++)
    {
        data_temp = src[i];
        if ((data_temp >= '0' && data_temp <= '9') || (data_temp >= 'A' && data_temp <= 'F') || (data_temp >= 'a' && data_temp <= 'f'))
        {
            if (data_temp == '0' && ((src[i + 1] == 'x') || (src[i + 1] == 'X')))
            {
                continue;
            }
            convert_data[convert_num++] = data_temp;
        }
        if ((convert_num == 1) && (i == data_len - 1))
        {
            // 最后一个数据是单数
            convert_data[0] = data_temp;
            convert_data[1] = '0';
            convert_num = 2;
        }
        if (convert_num != 2)
        {
            continue;
        }
        data_temp = 0;
        if (CONVERT_BIG_ENDIAN == type)
        {
            gStringConvertHexBigEndian(convert_data,&data_temp);
        }
        else
        {
            gStringConvertHexLittleEndian(convert_data,&data_temp);
        }

        convert_num = 0;
        dst[num++] = data_temp;
    }

    return num;
}

int gStringToHexBigendian(void *src,void *dst)
{
    return gStringToHexCommon((const char *)src,(uint8_t *)dst,CONVERT_LITTLE_ENDIAN);
}

int gStringToHexLittleendian(void *src,void *dst)
{
    return gStringToHexCommon((const char *)src,(uint8_t *)dst,CONVERT_BIG_ENDIAN);
}

void gStringConvertLowercase(void *src, void *dst)
{
    char *dst_p = dst;
    const char *src_p = src;
    int data_len = strlen(src);
    
    for (int i = 0; i < data_len; i ++)
    {
        dst_p[i] = src_p[i];
        if (dst_p[i] >= 'A' && dst_p[i] <= 'Z')
        {
            dst_p[i] += 32;
        }
    }
}

void gStringConvertUpper(void *src, void *dst)
{
    char *dst_p = dst;
    const char *src_p = src;
    int data_len = strlen(src);
    
    for (int i = 0; i < data_len; i ++)
    {
        dst_p[i] = src_p[i];
        if (dst_p[i] >= 'a' && dst_p[i] <= 'z')
        {
            dst_p[i] -= 32;
        }
    }
}
